﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using FeedAntGame.Support;
using FeedAntGame.Bus;

namespace FeedAntGame.Sprite
{
    class AntSprite : BaseSprite
    {
        Texture2D upImage;
        Texture2D downImage;
        Texture2D upRightImage;
        Texture2D downRightImage;
        Texture2D rightImage;
        Texture2D turnRightDownToDownImage;
        Status status;

        int MATRIX_WIDTH = 30;
        int MATRIX_HEIGHT = 20;
        int[,] matrix = new int[30, 20];
        int power;

        public int Power
        {
            get { return power; }
            set { power = value; }
        }

        public AntSprite(Texture2D downImage, Texture2D upImage, Texture2D rightImage, Texture2D rightUpImage,
        Texture2D rightDownImage, Texture2D turnRightDownToRight,Vector2 position, Point frameSize,
            int collisionOffset, Point currentFrame, Point sheetSize, Vector2 speed, string collisionCueName, int scoreValue,
            float scale, Game game)
            : base(downImage, position, frameSize, collisionOffset, currentFrame,
            sheetSize, speed, collisionCueName, scoreValue, scale)
        {

            this.downImage = downImage;
            this.downRightImage = rightDownImage;
            this.upImage = upImage;
            this.upRightImage = rightUpImage;
            this.downImage = downImage;
            this.turnRightDownToDownImage = turnRightDownToRight;
            this.rightImage = rightImage;
            this.origin = new Vector2(textureImage.Width / 2, textureImage.Height / 2);
            Viewport viewport = ((GameFA)game).Graphics.GraphicsDevice.Viewport;
            position.X = viewport.Width / 2;
            position.Y = viewport.Height / 2;
            frameSwitch = false;
            status = new Status();
            clearMatrix();
        }

        public AntSprite(Texture2D downImage, Texture2D upImage, Texture2D rightImage, Texture2D rightUpImage,
            Texture2D rightDownImage, Texture2D turnRightDownToRight, Vector2 position, Point frameSize,
            int collisionOffset, Point currentFrame, Point sheetSize, Vector2 speed,
            int millisecondsPerFrame, string collisionCueName, int scoreValue, float scale, Game game)
            : base(downImage, position, frameSize, collisionOffset, currentFrame,
            sheetSize, speed, millisecondsPerFrame, collisionCueName,scoreValue, scale)
        {
            this.downImage = downImage;
            this.downRightImage = rightDownImage;
            this.upImage = upImage;
            this.upRightImage = rightUpImage;
            this.downImage = downImage;
            this.turnRightDownToDownImage = turnRightDownToRight;
            this.rightImage = rightImage;
            this.Origin = new Vector2(frameSize.X/ 2, frameSize.Y / 2);
            Viewport viewport = ((GameFA)game).Graphics.GraphicsDevice.Viewport;
            position.X = viewport.Width / 2;
            position.Y = viewport.Height / 2;
            frameSwitch = false;
            status = new Status();
            clearMatrix();
        }

        public override Vector2 Direction
        {
            get
            {
                Vector2 moveDirection = Vector2.Zero;
                switch (status.Value)
                {
                    case Status.STOP:

                        break;
                    case Status.DOWN:
                        moveDirection.Y += 1;
                        break;

                    case Status.UP:
                        moveDirection.Y -= 1;
                        break;

                    case Status.LEFT:
                        moveDirection.X -= 1;
                        break;

                    case Status.RIGHT:
                        moveDirection.X += 1;
                        break;

                    case Status.UP_LEFT:
                        moveDirection.Y -= 1;
                        moveDirection.X -= 1;
                        break;

                    case Status.UP_RIGHT:
                        moveDirection.Y -= 1;
                        moveDirection.X += 1;
                        break;

                    case Status.DOWN_LEFT:
                        moveDirection.Y += 1;
                        moveDirection.X -= 1;
                        break;

                    case Status.DOWN_RIGHT:
                        moveDirection.Y += 1;
                        moveDirection.X += 1;
                        break;
                }
                return speed * moveDirection;
            }
        }

        public override void Update(GameTime gameTime, Rectangle clientBounds)
        {
            if (!targetPos.Equals(Vector2.Zero))
            {
                move(ref targetPos);
                this.position += this.Direction;
            }
            else
            {

            }
            base.Update(gameTime, clientBounds);
        }

        private Vector2 targetPos = Vector2.Zero;
        /************************************************************************/
        /*                  Move methods                                                                     */
        /************************************************************************/
        /// <summary>
        /// 
        /// </summary>
        /// <param name="targetPos"></param>
        /// <returns>True: move to targetPos. False: moving..</returns>
        public bool move(ref Vector2 targetPos)
        {
            
            this.targetPos = targetPos;
            if (Math.Abs(this.position.X - targetPos.X) < 1)
            {
                this.position.X = targetPos.X;
            }
            if (Math.Abs(this.position.Y - targetPos.Y) < 1)
            {
                this.position.Y = targetPos.Y;
            }
            if (this.position.X == targetPos.X && this.position.Y == targetPos.Y)
            {
                targetPos = Vector2.Zero;
                frameSwitch = false;
                return true;
            }
            // start moving
            frameSwitch = true;
            if (this.position.X < targetPos.X && this.position.Y < targetPos.Y)
            {
                moveDownRight();
            }
            if (this.position.X < targetPos.X && this.position.Y > targetPos.Y)
            {
                moveUpRight();
            }
            if (this.position.X > targetPos.X && this.position.Y < targetPos.Y)
            {
                moveDownLeft();
            }
            if (this.position.X > targetPos.X && this.position.Y > targetPos.Y)
            {
                moveUpLeft();
            }
            if (this.position.X == targetPos.X && this.position.Y > targetPos.Y)
            {
                moveUp();
            }
            if (this.position.X == targetPos.X && this.position.Y < targetPos.Y)
            {
                moveDown();
            }
            if (this.position.X < targetPos.X && this.position.Y == targetPos.Y)
            {
                moveRight();
            }
            if (this.position.X > targetPos.X && this.position.Y == targetPos.Y)
            {
                moveLeft();
            }
            return false;
        }
        private void moveDown()
        {
            if (status.Value != Status.DOWN)
            {
                this.textureImage = this.downImage;
                this.sheetSize = new Point(1, 3);
                this.rotation = 0;
                this.spriteEffect = SpriteEffects.None;
                status.Value = Status.DOWN;
                this.animateType = ANIMATE_TYPE.CIRCLE;
            }
            
        }
        private void moveUp()
        {
            if (status.Value != Status.UP)
            {
                this.textureImage = this.upImage;
                this.sheetSize = new Point(1, 3);
                this.rotation = 0;
                this.spriteEffect = SpriteEffects.None;
                status.Value = Status.UP;
                this.animateType = ANIMATE_TYPE.CIRCLE;
            }
        }
        private void moveUpLeft()
        {
            if (status.Value != Status.UP_LEFT)
            {
                this.textureImage = this.upRightImage;
                this.sheetSize = new Point(1, 3);
                this.spriteEffect = SpriteEffects.FlipHorizontally;
                status.Value = Status.UP_LEFT;
                this.animateType = ANIMATE_TYPE.CIRCLE;
            }
        }
        private void moveUpRight()
        {
            if (status.Value != Status.UP_RIGHT)
            {
                this.textureImage = this.upRightImage;
                this.sheetSize = new Point(1, 3);
                this.rotation = 0;
                this.spriteEffect = SpriteEffects.None;
                status.Value = Status.UP_RIGHT;
                this.animateType = ANIMATE_TYPE.CIRCLE;
            }
        }
        private void moveRight()
        {
            if (status.Value != Status.RIGHT)
            {
                this.textureImage = this.rightImage;
                this.sheetSize = new Point(1, 3);
                this.rotation = 0;
                this.spriteEffect = SpriteEffects.None;
                status.Value = Status.RIGHT;               
                this.animateType = ANIMATE_TYPE.CIRCLE;
            }
        }
        private void moveLeft()
        {
            if (status.Value != Status.LEFT)
            {
                this.textureImage = this.rightImage;
                this.sheetSize = new Point(1, 3);
                this.rotation = 0;
                this.spriteEffect = SpriteEffects.FlipHorizontally;
                status.Value = Status.LEFT;
                this.animateType = ANIMATE_TYPE.CIRCLE;
            }
        }
        private void moveDownRight()
        {
            if (status.Value != Status.DOWN_RIGHT)
            {
                this.textureImage = this.downRightImage;
                this.sheetSize = new Point(1, 3);
                this.rotation = 0;
                this.spriteEffect = SpriteEffects.None;
                status.Value = Status.DOWN_RIGHT;
                this.animateType = ANIMATE_TYPE.CIRCLE;                
            }

        }
        private void moveDownLeft()
        {
            if (status.Value != Status.DOWN_LEFT)
            {
                this.textureImage = this.downRightImage;
                this.sheetSize = new Point(1, 3);
                this.rotation = 0;
                this.spriteEffect = SpriteEffects.FlipHorizontally;
                this.position.X -= this.Width / 3;
                status.Value = Status.DOWN_LEFT;
                this.animateType = ANIMATE_TYPE.CIRCLE;   
            }
        }

        private void turnRightDownToDown()
        {
            this.textureImage = this.turnRightDownToDownImage;
            this.sheetSize = new Point(1, 5);
            this.rotation = 0;
            this.spriteEffect = SpriteEffects.None;            
        }

        private void turnLeftDownToDown()
        {
            this.textureImage = this.turnRightDownToDownImage;
            this.sheetSize = new Point(1, 5);
            this.rotation = 90;
            this.spriteEffect = SpriteEffects.None;
        }

        //Find way to candy
        public List<BaseSprite> floodList = new List<BaseSprite>();
        public List<BaseSprite> bestWayList = new List<BaseSprite>();
        public void FindWay(BaseSprite start, BaseSprite end, MapCTL map)
        {
            this.matrix = map.matrix;
            this.MATRIX_WIDTH = map.MATRIX_WIDTH;
            this.MATRIX_HEIGHT = map.MATRIX_HEIGHT;

            // initialize flood list
            floodList.Add(start);
            bool finish = false;
            int max_value_of_matrix = int.MinValue;
            for (int i = 0; i < floodList.Count; i++)
            {
                if (finish)
                    break;
                BaseSprite s = floodList.ElementAt(i);
                int XPos = (int)getPosInMatrix(s,map).X;
                int YPos = (int)getPosInMatrix(s,map).Y;
                for (int j = 0; j < 4; j++)
                {
                    BaseSprite s1 = null;
                    int x = -1, y = -1;
                    switch (j)
                    {
                        case 0: s1 = getSpriteAtPos(new Vector2(x = XPos - 1, y = YPos), map); break;
                        case 1: s1 = getSpriteAtPos(new Vector2(x = XPos + 1, y = YPos), map); break;
                        case 2: s1 = getSpriteAtPos(new Vector2(x = XPos, y = YPos - 1), map); break;
                        case 3: s1 = getSpriteAtPos(new Vector2(x = XPos, y = YPos + 1), map); break;
                    }
                    if (s1 != null)
                    {
                        if (floodList.IndexOf(s1) < 0)
                        {
                            floodList.Add(s1);
                            matrix[x, y] = matrix[XPos, YPos] + 1;
                            if (matrix[x, y] > max_value_of_matrix)
                            {
                                max_value_of_matrix = matrix[x, y];
                            }
                            if (s1.Equals(end))
                            {
                                finish = true;
                                break;
                            }
                        }
                    }

                }
            }

            //add to best way list : from end to start
            bestWayList.Add(floodList.ElementAt(floodList.Count - 1));
            for (int i = 0; i < bestWayList.Count; i++)
            {
                BaseSprite s = bestWayList.ElementAt(i);
                Vector2 pos = getPosInMatrix(s,map);
                //find other less than 1 value in matrix (from 4 position beside it)
                Vector2 lessPos = lessThanOne(pos);
                if (matrix[(int)lessPos.X, (int)lessPos.Y] != 0)
                {
                    bestWayList.Add(getSpriteAtPos(lessPos,map));
                }
                else
                {
                    break;
                }


            }
        }

        /// <summary>
        /// Get barrierPos of s BaseSprite in matrix
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        private Vector2 getPosInMatrix(BaseSprite s, MapCTL map)
        {
            Vector2 ret = Vector2.Zero;
            if (s != null)
            {
                ret.X = (int)(s.Position.X - map.Position.X) / 22;
                ret.Y = (int)(s.Position.Y - map.Position.Y) / 30;
            }

            return ret;
        }
        /// <summary>
        /// Get BaseSprite at barrierPos in matrix 
        /// </summary>
        /// <param name="barrierPos"></param>
        /// <returns></returns>
        private BaseSprite getSpriteAtPos(Vector2 pos, MapCTL map)
        {

            for (int i = 0; i < map.ListRoad.Count; i++)
            {
                BaseSprite s = (BaseSprite)map.ListRoad.ElementAt(i);
                s.Position -= Vector2.Zero;
                if (getPosInMatrix(s,map) == pos)
                {
                    return s;
                }
            }
            return null;
        }

        /// <summary>
        /// Restore all cell in matrix to 0
        /// </summary>
        public void clearMatrix()
        {
            for (int i = 0; i < matrix.GetLength(0); i++)
            {
                for (int j = 0; j < matrix.GetLength(1); j++)
                {
                    matrix[i, j] = 0;
                }
            }
        }

        /// <summary>
        /// Find 1 of 4 near position of "barrierPos" param. Which is have value in matrix less than one.
        /// </summary>
        /// <param name="barrierPos"></param>
        /// <returns></returns>
        private Vector2 lessThanOne(Vector2 pos)
        {
            Vector2 ret = Vector2.Zero;
            int x = (int)pos.X;
            int y = (int)pos.Y;
            for (int i = 0; i < 4; i++)
            {
                switch (i)
                {
                    case 0:
                        if (x > 0)
                            if (matrix[x, y] == matrix[x - 1, y] + 1)
                                ret = new Vector2(x - 1, y);
                        break;
                    case 1:
                        if (x < MATRIX_WIDTH - 1)
                            if (matrix[x, y] == matrix[x + 1, y] + 1)
                                ret = new Vector2(x + 1, y);
                        break;
                    case 2:
                        if (y > 0)
                            if (matrix[x, y] == matrix[x, y - 1] + 1)
                                ret = new Vector2(x, y - 1);
                        break;
                    case 3:
                        if (y < MATRIX_HEIGHT - 1)
                            if (matrix[x, y] == matrix[x, y + 1] + 1)
                                ret = new Vector2(x, y + 1);
                        break;
                }
            }
            return ret;
        }
    }
}
